/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.haiberg.osgi.bundle.reportservice;

import com.crystaldecisions.reports.sdk.ParameterFieldController;
import com.crystaldecisions.reports.sdk.ReportClientDocument;
import com.crystaldecisions.sdk.occa.report.exportoptions.CharacterSeparatedValuesExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ExportOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ICharacterSeparatedValuesExportFormatOptions.ExportMode;
import com.crystaldecisions.sdk.occa.report.exportoptions.ReportExportFormat;
import com.crystaldecisions12.sdk.occa.report.application.OpenReportOptions;
import com.haiberg.osgi.bundle.reportservice.utils.ReportUtils;
import com.haiberg.osgi.bundle.reportservice.utils.Utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 *
 * @author gyue
 */
public class ReportRequestHandler {

    private String xmlData = "";
    private String userName = "";
    private String reportFileName = "";
    private int exportFormat = 0;
    private String dbUsername = "";
    private String dbUserpwd = "";
    private boolean useWrapper = false;
    ReportExportInfo exportInfo = null;

    // 2 Tage
    private final int timoutInMilisec = 2 * 24 * 3600 * 1000;

    private List<ReportParamValue> reportParams = null;

    public ReportRequestHandler(){
    
    }
    public ReportRequestHandler(String xmlData) {
        //	parse XML and get Params
        this.xmlData = xmlData;
        reportParams = new LinkedList<ReportParamValue>();
        parseXmlData();
    }
    public void init(){
        if(StringUtils.isNotBlank(this.xmlData)){
            reportParams = new LinkedList<ReportParamValue>();
            parseXmlData();
        }else if(exportInfo != null){
            this.reportParams = exportInfo.getParams();
            this.userName = exportInfo.getAppUsername();
            this.dbUsername = exportInfo.getDbUsername();
            this.dbUserpwd = exportInfo.getDbUserpwd();
            this.reportFileName = exportInfo.getReportName();
            this.exportFormat = exportInfo.getExportFormat();
        }
    }
    
    public void setXMLData(String xmlData){
        this.xmlData = xmlData;
    }
    public void setExportInfo(ReportExportInfo exportInfo){
        this.exportInfo = exportInfo;
    }

    public String getReportFileName() {
        return this.reportFileName;
    }

    private void parseXmlData() {
        try {

            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(new InputSource(new java.io.StringReader(xmlData)));

            userName = getDocParamValue(doc, "appUsername");
            dbUsername = getDocParamValue(doc, "username");
            dbUserpwd = getDocParamValue(doc, "userpwd");
            reportFileName = getDocParamValue(doc, "reportName");
            exportFormat = Integer.parseInt(getDocParamValue(doc, "reportFormat"));
            useWrapper = "1".equals(getDocParamValue(doc, "useWrapper"));

            NodeList paramsList = doc.getElementsByTagName("param");
            for (int i = 0; i < paramsList.getLength(); i++) {
                Node paramNode = paramsList.item(i);
                Integer id = Integer.parseInt(paramNode.getAttributes().getNamedItem("id").getNodeValue());
                String name = "";
                String value = "";
                Integer type = 0;

                for (int j = 0; j < paramNode.getChildNodes().getLength(); j++) {
                    if (paramNode.getChildNodes().item(j).getNodeName().equals("name")) {
                        name = paramNode.getChildNodes().item(j).getFirstChild().getNodeValue();
                    }

                    if (paramNode.getChildNodes().item(j).getNodeName().equals("value")) {
                        value = paramNode.getChildNodes().item(j).getFirstChild().getNodeValue();
                    }

                    if (paramNode.getChildNodes().item(j).getNodeName().equals("type")) {
                        type = Integer.parseInt(paramNode.getChildNodes().item(j).getFirstChild().getNodeValue());
                    }
                }

                reportParams.add(new ReportParamValue(id, name, value, type));
            }

        } catch (Exception es) {
            es.printStackTrace();
        }

    }

    private String getDocParamValue(Document doc, String paramName) {
        try {
            NodeList listOfNodes = doc.getElementsByTagName(paramName);
            if (listOfNodes.getLength() > 0) {
                Node node = listOfNodes.item(0);
                return node.getFirstChild().getNodeValue();
            }
        } catch (Exception e) {
        }

        return "";
    }

    public InputStream getReportDataAsStream()
            throws Exception {
        this.init();
        if (useWrapper) {
            return doPerformWrapperRequest();
        }
        
        ExportOptions exportOptions = null;
        ReportExportFormat exportFormatType;
        // Set ExportType
        switch (exportFormat) {
            case 0:
                exportFormatType = ReportExportFormat.PDF;
                break;

            case 1:
                exportFormatType = ReportExportFormat.MSExcel;
                break;

            case 2:
                exportFormatType = ReportExportFormat.recordToMSExcel;
                break;

            case 3:
                exportFormatType = ReportExportFormat.MSWord;
                break;

            case 4:
                exportFormatType = ReportExportFormat.RTF;
                break;

            case 5: //exportFormatType = CrystalDecisions.Shared.ExportFormatType.Text;
                exportFormatType = ReportExportFormat.characterSeparatedValues;
                 {
                    exportOptions = new ExportOptions();
                    exportOptions.setExportFormatType(exportFormatType);
                    CharacterSeparatedValuesExportFormatOptions csvOpts = new CharacterSeparatedValuesExportFormatOptions();
                    csvOpts.setDelimiter("\"");
                    csvOpts.setSeparator(";");
                    csvOpts.setExportMode(ExportMode.standardMode);
                    exportOptions.setFormatOptions(csvOpts);
                }
                break;

            case 6:
                exportFormatType = ReportExportFormat.characterSeparatedValues;
                 {
                    exportOptions = new ExportOptions();
                    exportOptions.setExportFormatType(exportFormatType);
                    CharacterSeparatedValuesExportFormatOptions csvOpts = new CharacterSeparatedValuesExportFormatOptions();
                    csvOpts.setDelimiter("\"");
                    csvOpts.setSeparator(";");
                    csvOpts.setExportMode(ExportMode.standardMode);
                    exportOptions.setFormatOptions(csvOpts);
                }
                break;

            default:
                exportFormatType = ReportExportFormat.PDF;
                break;
        }

		//Open report.			
        // Export to Stream
        ByteArrayInputStream byteArrayInputStream;
        ReportClientDocument reportClientDoc = null;
        try {
            reportClientDoc = new ReportClientDocument();
            String reportFile = ReportUtils.getReportAbsolutePath(reportFileName+".rpt",userName);
            if(!reportFile.startsWith("/")){
                reportFile = "/" + reportFile;
            }
            reportClientDoc.open(reportFile, OpenReportOptions._discardSavedData);

            // set password
            reportClientDoc.getDatabaseController().logon(dbUsername, dbUserpwd);

            // set params
            ParameterFieldController paramFieldController = reportClientDoc.getDataDefController().getParameterFieldController();
            for (ReportParamValue rpv : reportParams) {
                Object value = rpv.getValue();

                switch (rpv.fieldType) {
                    case 2: // int
                        if (Utils.isDouble(rpv.getValue())) {
                            value = Utils.getDouble(rpv.getValue()).intValue();
                        }
                        break;

                    case 3: // double
                        if (Utils.isDouble(rpv.getValue())) {
                            value = Utils.getDouble(rpv.getValue());
                        }
                        break;

                    case 4: // boolean
                        value = Utils.getBoolean(rpv.getValue());
                        break;

                    case 5: // Date			
                        if (Utils.getDateOrDateTime(rpv.getValue()) != null) {
                            value = Utils.getDateOrDateTime(rpv.getValue());
                        }

                        break;

                    default:
                        break;
                }
                // Set FieldValue
                try {
                    paramFieldController.setCurrentValue("", rpv.getFieldName(), value);
                } catch (Exception e) {
                }
            }

            if (exportOptions != null) {
                byteArrayInputStream = (ByteArrayInputStream) reportClientDoc.getPrintOutputController().export(exportOptions);
            } else {
                byteArrayInputStream = (ByteArrayInputStream) reportClientDoc.getPrintOutputController().export(exportFormatType);
            }

            return byteArrayInputStream;

        } catch (Exception e) {
            e.printStackTrace();
            //Error try to print with wrapper...
            return doPerformWrapperRequest();
        } finally {
            if (reportClientDoc != null) {
                //Release report.
                reportClientDoc.close();
            }
        }

    }

    private InputStream doPerformWrapperRequest() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            Socket socket = new Socket("localhost", 19000);
            OutputStream out = socket.getOutputStream();
            out.write(xmlData.getBytes());
            out.flush();

            InputStream in = socket.getInputStream();

            Date startReadDate = new Date();
            while (((new Date()).getTime() - startReadDate.getTime()) < timoutInMilisec && in.available() < 1) {
                Thread.sleep(100);
            }

            byte[] buffer = new byte[1024];
            int readed = 0;
            while ((readed = in.read(buffer, 0, buffer.length)) != -1) {
                baos.write(buffer, 0, readed);
            }
            baos.close();
            socket.close();

            // Create OutPutStream
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());

            return byteArrayInputStream;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static void main(String[] args){
        ReportRequestHandler requestHandler = new ReportRequestHandler();
        try {
            requestHandler.getReportDataAsStream();
        } catch (Exception ex) {
            Logger.getLogger(ReportRequestHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
